﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

using log4net.Appender;
using log4net.Core;

namespace TwitterAppender
{
    public class TwitterAppender : AppenderSkeleton
    {
        #region Private Members

        private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";
        private const string REQUEST_METHOD = "POST";

        // The source attribute has been removed from the Twitter API, 
        // unless you're using OAuth.
        // Even if you are using OAuth, there's still an approval process.  
        // Not worth it; "API" will work for now!
        // private const string TWITTER_SOURCE_NAME = "Log4Net";

        private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";

        private string _twitterUserName;
        private string _twitterPassword;

        /// <summary>
        /// This is where we're posting the actual event as a tweet.  
        /// I was originally thinking of using REST-ful WCF, but, to keep things simple
        /// and to reduce dependencies on other technologies, I decided to just use a traditional web request.
        /// </summary>
        /// <param name="eventToPost_">The event to post.</param>
        private void PostLoggingEvent(LoggingEvent eventToPost_)
        {
            // The base RenderLoggingEvent method converts the event to a string 
            // based on the layout specified in the configuration file.
            // It's important to encode the tweet so that the request URL remains valid.

            var updateRequest = HttpWebRequest.Create(String.Format(TWITTER_UPDATE_URL_FORMAT, 
                HttpUtility.UrlEncode(RenderLoggingEvent(eventToPost_).ToTweet()))) as HttpWebRequest;

            updateRequest.ContentLength = 0;
            updateRequest.ContentType = REQUEST_CONTENT_TYPE;
            updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
            updateRequest.Method = REQUEST_METHOD;

            // This is in idiosyncracy with the Twitter API.
            updateRequest.ServicePoint.Expect100Continue = true;

            var updateResponse = updateRequest.GetResponse() as HttpWebResponse;

            if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
                throw new ApplicationException(String.Format("An error occured while invoking the Twitter REST API [Response Code: {0}]; unable to proceed.", updateResponse.StatusCode));
        }

        #endregion

        #region Public Members

        /// <summary>
        /// This is the password for the Twitter account that is to be used.
        /// Fortunately, the Log4Net framework automatically populates 
        /// this property for us based on the configuration.
        /// </summary>
        public string TwitterPassword
        {
            get
            {
                // We need a password.
                if (String.IsNullOrEmpty(_twitterPassword))
                    throw new ApplicationException("Twitter account password not specified; unable to proceed.");

                return _twitterPassword;
            }
            set
            {
                _twitterPassword = value;
            }
        }

        /// <summary>
        /// This is the user name for the Twitter account that is to be used.
        /// Fortunately, the Log4Net framework automatically populates 
        /// this property for us based on the configuration.
        /// </summary>
        public string TwitterUserName
        {
            get
            {
                // We need a user name.
                if (String.IsNullOrEmpty(_twitterUserName))
                    throw new ApplicationException("Twitter account user name not specified; unable to proceed.");

                return _twitterUserName;
            }
            set
            {
                _twitterUserName = value;
            }
        }

        #endregion

        #region Protected Members

        /// <summary>
        /// This is the main entry point for the Log4Net framework.
        /// </summary>
        /// <param name="loggingEvent_">The event to append.</param>
        protected override void Append(LoggingEvent loggingEvent_)
        {
            try
            {
                if (loggingEvent_ == null)
                    throw new ArgumentNullException("loggingEvent_");

                PostLoggingEvent(loggingEvent_);
            }
            catch (Exception ex_)
            {
                // Since we're already inside the event logging framework, 
                // let Log4Net handle this one.  
                // We don't want to get stuck in a loop!
                ErrorHandler.Error("An error occured while posting the provided event; unable to proceed.", ex_);
            }
        }

        #endregion
    }
}